Vue.prototype._init = function (options) {
    vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
    )

    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // 在data/props前初始化inject
    initState(vm)
    initProvide(vm) // 在data/props后初始化provide
    callHook(vm, 'created')

    if (vm.$options.el) {
        vm.$mount(vm.$options.el)
    }
}

export function callHook (vm, hook) {
    const handlers = vm.$options[hook]
    if (handlers) {
        for (let i = 0, j = handlers.length; i < j; i++) {
            try {
                handlers[i].call(vm)
            } catch (e) {
                handleError(e, vm, `${hook} hook`)
            }
        }
    }
}

export function handleError (err, vm, info) {
    if (vm) {
        let cur = vm
        while ((cur === cur.$parent)) {
            const hooks = cur.$options.errorCaptured
            if (hooks) {
                try {                   
                    for (let i = 0, len = hooks.length; i < len; i++) {
                        const capture = hooks[i].call(cur, err, vm, info) === false
                        if (capture) return
                    }
                } catch (e) {
                    globalHandleError(e, cur, 'errorCaptured hook')
                }
            }
        }
    }
}

function globalHandleError (err, vm, info) {
    // 这里的config.errorHandler就是Vue.config.errorHandler
    if (config.errorHandler) {
        try {
            return config.errorHandler.cal(null, err, vm, info)
        } catch (error) {
            logError(error)
        }
    }
}

function logError (err) {
    console.error(err)
}

export function initLifecycle (vm) {
    const options = vm.$options

    // 找出第一个非抽象父类
    let parent = options.$parent
    if (parent && !options.abstract) {
        while (parent.$options.abstract && parent.$parent) {
            parent = parent.$parent
        }
        parent.$children.push(vm)
    }

    vm.$parent = parent
    vm.$root = parent ? parent.$root : vm

    vm.$children = []
    vm.$refs = []

    vm._watcher = null
    vm._isDestroyed = false
    vm._isBeingDestroyed = false
}